﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using System.Xml.Linq;
using System.Configuration;
using System.Runtime.InteropServices;
using System.Threading;

namespace SQLDiag.UI
{
    public partial class FormMain : Form
    {
        // Counters for the number of checked child nodes -- added by shen 12/17/2010
        private int[] perfNodeCheckedCount = new int[1000];
        private int[] profilerNodeCheckedCount = new int[1000];
        private String curFileName = "SQLDiag.xml"; // current configure filename -- added by Shen 12/17/2010 
        private string sqlDiagPath = "";
        private Process p = null;

        private const int SW_HIDE = 0;
        private const int SW_SHOWMINIMIZED = 2;


        [DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
        public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

        // Activate an application window.
        [DllImport("USER32.DLL")]
        public static extern bool SetForegroundWindow(IntPtr hWnd);

        [DllImport("user32.dll")]
        public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

        public FormMain()
        {
            InitializeComponent();

            this.tvProfiler.AfterCheck += new TreeViewEventHandler(tvProfiler_AfterCheck);
            this.tvPerfmonCounters.AfterCheck += new TreeViewEventHandler(tvPerfmonCounters_AfterCheck);
        }

        private void FormMain_Load(object sender, EventArgs e)
        {
            for (int i = 0; i < 1000; i++)
            {
                perfNodeCheckedCount[i] = 0;
                profilerNodeCheckedCount[i] = 0;
            }

            //load default SQLDiag.xml
            XDocument doc = XDocument.Load(curFileName);

            //preset values according to opened file -- added by Shen 12/17/2010
            var blockElm = from bl in doc.Descendants("BlockingCollector")
                           select bl;
            cbBlocking.Checked = (blockElm.First().Attributes("enabled").First().Value == "true");

            var perfMonElm = from pf in doc.Descendants("PerfmonCollector")
                             select pf;
            cbPerfmon.Checked = (perfMonElm.First().Attributes("enabled").First().Value == "true");

            var profilerElm = from profiler in doc.Descendants("ProfilerCollector")
                              select profiler;
            cbProfiler.Checked = (profilerElm.First().Attributes("enabled").First().Value == "true");

            var machineNameElm = from mn in doc.Descendants("Machine")
                                 select mn;
            tbMachineName.Text = machineNameElm.First().Attributes("name").First().Value;

            var instanceNameElm = from inst in doc.Descendants("Instance")
                                  select inst;
            tbInstanceName.Text = instanceNameElm.First().Attributes("name").First().Value;

            LoadPerfmon(doc);

            LoadProfiler(doc);

            sqlDiagPath = ConfigurationManager.AppSettings["SQLDiagPath"].ToString();

            this.KeyPreview = true;
        }//form_load

        private void LoadProfiler(XDocument doc)
        {
            this.tvProfiler.Nodes.Clear();

            //add root node
            TreeNode tnRoot = new TreeNode("Profiler");
            this.tvProfiler.Nodes.Add(tnRoot);

            //load profiler events
            var profilerEventTypes = from pet in doc.Descendants("EventType")
                                     select pet;

            foreach (var item in profilerEventTypes)
            {
                TreeNode tn = new TreeNode(item.Attributes("name").First().Value);
                
                //initialize the checked values -- Added by Shen 12/12/2010
                if (item.Attributes("enabled").Count() != 0)
                    if (item.Attributes("enabled").First().Value == "true") 
                    {
                        tn.BackColor = System.Drawing.SystemColors.ControlDark;
                        tn.Checked = true;
                    }
                   
                tnRoot.Nodes.Add(tn);

                //load profiler events under event type
                var profilerEvents = from pe in item.Descendants("Event")
                                     select pe;

                foreach (var itemPe in profilerEvents)
                {
                    TreeNode tnPe = new TreeNode(itemPe.Attributes("name").First().Value);

                    //initialize the checked values -- Added by Shen 12/12/2010
                    if (itemPe.Attributes("enabled").Count() != 0)
                        if (itemPe.Attributes("enabled").First().Value == "true")
                        {
                            if (!tn.Checked)
                                tn.BackColor = System.Drawing.SystemColors.ControlDark;

                            profilerNodeCheckedCount[tn.Index]++;
                            tnPe.Checked = true;
                        }
                    tn.Nodes.Add(tnPe);
                }
            }

            tnRoot.Expand();
        }

        void highlightParent(TreeViewEventArgs e, int[] nodeCheckedCount)
        {
            if (e.Node.Level == 2) // If this a leaf node, highlight the parent -- Added by Shen 12/17/2010
            {
                if (e.Node.Checked)
                {
                    e.Node.Parent.BackColor = System.Drawing.SystemColors.ControlDark;
                    nodeCheckedCount[e.Node.Parent.Index]++;
                }
                else
                {
                    if (--nodeCheckedCount[e.Node.Parent.Index] <= 0)
                        e.Node.Parent.BackColor = System.Drawing.SystemColors.Control;
                }
            }

            if (e.Node.Level == 1) // For second level node, highlight itself -- added by shen 12/17/2010
            {
                if (e.Node.Checked)
                    e.Node.BackColor = System.Drawing.SystemColors.ControlDark;
                else
                    e.Node.BackColor = System.Drawing.SystemColors.Control;
            }
        }

        void tvProfiler_AfterCheck(object sender, TreeViewEventArgs e)
        {
            ToggleCheckbox(e);
            highlightParent(e, profilerNodeCheckedCount);
            this.toolBtnSave.Enabled = true;
        }

        private void LoadPerfmon(XDocument doc)
        {            
            this.tvPerfmonCounters.Nodes.Clear();

            //add root node
            TreeNode tnRoot = new TreeNode("Perfmon Objects");
            this.tvPerfmonCounters.Nodes.Add(tnRoot);

            //load perfmon objects
            var perfObjects = from po in doc.Descendants("PerfmonObject")
                              select po;

            foreach (var item in perfObjects)
            {
                TreeNode tn = new TreeNode(item.Attributes("name").First().Value);
                
                //initialize the checked values -- Added by Shen 12/12/2010
                if (item.Attributes("enabled").Count() != 0)
                    if (item.Attributes("enabled").First().Value == "true")
                    {
                        tn.BackColor = System.Drawing.SystemColors.ControlDark;
                        tn.Checked = true;
                    }
                    
                tnRoot.Nodes.Add(tn);

                //load perfmon counters under the object
                var perfmonCounters = from pc in item.Descendants("PerfmonCounter")
                                      select pc;

                foreach (var itemPc in perfmonCounters)
                {
                    TreeNode tnPc = new TreeNode(itemPc.Attributes("name").First().Value);

                    //initialize the checked values -- Added by Shen 12/12/2010
                    if (itemPc.Attributes("enabled").Count() != 0)
                        if (itemPc.Attributes("enabled").First().Value == "true")
                        {
                            if (!tn.Checked)
                                tn.BackColor = System.Drawing.SystemColors.ControlDark;

                            perfNodeCheckedCount[tn.Index]++;
                            tnPc.Checked = true;
                        }

                    tn.Nodes.Add(tnPc);
                }
            }

            tnRoot.Expand();
        }

        private void tvPerfmonCounters_AfterCheck(object sender, TreeViewEventArgs e)
        {
            ToggleCheckbox(e);
            highlightParent(e, perfNodeCheckedCount);
            this.toolBtnSave.Enabled = true;
        }

        private static void ToggleCheckbox(TreeViewEventArgs e)
        {
            TreeNodeCollection tna = e.Node.Nodes;
            bool isChecked = e.Node.Checked;
            
            foreach (TreeNode tn in tna)
            {
                tn.Checked = isChecked;
            }
        }

        private void cbPerfmon_CheckedChanged(object sender, EventArgs e)
        {
            //this.tvPerfmonCounters.CheckBoxes = this.cbPerfmon.Checked;
            this.tvPerfmonCounters.Enabled = this.cbPerfmon.Checked;
            this.tvPerfmonCounters.Nodes[0].Expand();
            this.toolBtnSave.Enabled = true;
        }

        private void cbProfiler_CheckedChanged(object sender, EventArgs e)
        {
            //this.tvProfiler.CheckBoxes = this.cbProfiler.Checked; -- changing CheckBoxes value will lose all checked items?? 
            this.tvProfiler.Enabled = this.cbProfiler.Checked;
            this.tvProfiler.Nodes[0].Expand();

            this.toolBtnSave.Enabled = true;
        }

        private void toolBtnSave_Click(object sender, EventArgs e)
        {
            string fileName = "";

            // user select filepath and file name -- added by Shen 12/12/2010
            saveFileDialog1.ShowDialog();
            fileName = saveFileDialog1.FileName;

            //user cancel the file dialogue -- added by Shen 12/12/2010
            if (fileName == "")
                return;

            FileSave(fileName);
        }

        private void FileSave(string fileName)
        {
            try
            {
                //save result back to SQLDiag.xml
                XDocument doc = XDocument.Load("SQLDiag.xml");

                // save machine name - added by Shen 12/12/2010 
                if (tbMachineName.Text == "")
                {
                    MessageBox.Show("Invalid Machine Name!");
                    return;
                }
                else
                    doc.Descendants("Machine").First().SetAttributeValue("name", tbMachineName.Text);

                // save instance name - added by Shen 12/12/2010 
                if (tbInstanceName.Text == "")
                {
                    MessageBox.Show("Invalid Instance Name!");
                    return;
                }
                else
                    doc.Descendants("Instance").First().SetAttributeValue("name", tbInstanceName.Text);

                //save blocking collector's checkbox - added by Shen 12/12/2010 
                if (cbBlocking.Checked)
                    doc.Descendants("BlockingCollector").First().SetAttributeValue("enabled", "true"); 

                if (cbPerfmon.Checked)
                {
                    //first enable perfmon collector -- added by Shen 12/12/2010
                    doc.Descendants("PerfmonCollector").First().SetAttributeValue("enabled", "true");

                    //save Perfmon counters
                    //get the root node and perfmon objects nodes below it
                    TreeNodeCollection tnc = this.tvPerfmonCounters.Nodes[0].Nodes;

                    //for each perfmon object, set perfmon counters
                    foreach (TreeNode tn in tnc)
                    {
                        TreeNodeCollection tncPerfCounters = tn.Nodes;

                        //find the perf object and set "enabled" attribute accondingly -- added by shen 12/17/2010
                        IEnumerable<XElement> perfObj = from po in doc.Descendants("PerfmonObject")
                                                        where po.Attributes("name").First().Value == tn.Text
                                                        select po;
                        perfObj.First().SetAttributeValue("enabled", tn.Checked);

                        //find the perfmon counter and set the "enabled" attribute to be the value of treenode checked property
                        foreach (TreeNode tn1 in tncPerfCounters)
                        {
                            IEnumerable<XElement> perfCounter = from pc in doc.Descendants("PerfmonCounter")
                                                                where pc.Attributes("name").First().Value == tn1.Text && pc.Parent.Attributes("name").First().Value == tn.Text
                                                                select pc;

                            perfCounter.First().SetAttributeValue("enabled", tn1.Checked);
                        }
                    }
                }

                if (cbProfiler.Checked)
                {
                    //first enable profiler collector- - added by Shen 12/12/2010
                    doc.Descendants("ProfilerCollector").First().SetAttributeValue("enabled", "true");

                    //save profiler events
                    //get the root node and all profiler event types nodes below it
                    //events/eventType/event
                    TreeNodeCollection tncProfilerEventTypes = this.tvProfiler.Nodes[0].Nodes;

                    //for each event type, set event "enabled" property based on checkbox value
                    foreach (TreeNode tn in tncProfilerEventTypes)
                    {
                        TreeNodeCollection tncEvents = tn.Nodes;

                        //find the event type and set "enabled" attribute accondingly -- added by shen 12/17/2010
                        IEnumerable<XElement> profilerEvtType = from evt in doc.Descendants("EventType")
                                                                where evt.Attributes("name").First().Value == tn.Text
                                                                select evt;
                        profilerEvtType.First().SetAttributeValue("enabled", tn.Checked);

                        //find the event and set the "enabled" attribute to be the value of treenode checked property
                        foreach (TreeNode tn1 in tncEvents)
                        {
                            IEnumerable<XElement> profilerEvent = from ev in doc.Descendants("Event")
                                                                  where ev.Attributes("name").First().Value == tn1.Text && ev.Parent.Attributes("name").First().Value == tn.Text
                                                                  select ev;

                            profilerEvent.First().SetAttributeValue("enabled", tn1.Checked);
                        }
                    }
                }

                doc.Save(fileName);

                this.toolBtnSave.Enabled = false;
                // change the message -- modified by shen 12/12/2010
                MessageBox.Show(fileName + " is saved.", "SQLDiag.XML", MessageBoxButtons.OK);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error Message", MessageBoxButtons.OK);
            }
        }

        private void cbBlocking_CheckedChanged(object sender, EventArgs e)
        {
            this.toolBtnSave.Enabled = true;
        }

        private void tbMachineName_TextChanged(object sender, EventArgs e)
        {
            this.toolBtnSave.Enabled = true;
        }

        private void tbInstanceName_TextChanged(object sender, EventArgs e)
        {
            this.toolBtnSave.Enabled = true;
        }

        private void toolButtonStart_Click(object sender, EventArgs e)
        {
            this.toolButtonStart.Enabled = false;
            this.toolBtnStop.Enabled = true;
            try
            {
                //set focus to output tab page
                this.tabMain.SelectedIndex = 2;

                //save the current configuration to a temp file
                string sTemp = @"C:\temp";
                String tempFileName = sTemp + @"\SQLDiag" + Guid.NewGuid().ToString() + ".xml";
                this.FileSave(tempFileName);

                p = new Process();

                ProcessStartInfo psi = new ProcessStartInfo();
                psi.FileName = sqlDiagPath;

                string sOutputFolder = sTemp + @"\SQLDiagOutput";
                DirectoryInfo di = new DirectoryInfo(sOutputFolder);
                sOutputFolder = di.CreateSubdirectory(Guid.NewGuid().ToString()).FullName;

                psi.Arguments = string.Format("/i \"{0}\" /o \"{1}\"", tempFileName, sOutputFolder);
                psi.UseShellExecute = false;
                psi.RedirectStandardOutput = true;
                psi.CreateNoWindow = false;

                p.EnableRaisingEvents = true;
                p.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived);
                p.Exited += new EventHandler(p_Exited);
                p.StartInfo = psi;

                p.Start();

                if (p.MainWindowHandle != IntPtr.Zero)
                {
                    ShowWindow(p.MainWindowHandle, 0);
                }

                p.BeginOutputReadLine();
            }
            catch (Exception ex)
            {
                this.rtbExecStatus.Text = ex.Message;
            }
        }



        void p_Exited(object sender, EventArgs e)
        {
            if (this.InvokeRequired)
            {
                Invoke(new EventHandler(p_Exited), new object[] { sender, e });
                return;
            }
            this.rtbExecStatus.Text += "Finished";
            this.toolButtonStart.Enabled = true;
            this.toolBtnStop.Enabled = false;
        }

        void p_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            if (this.InvokeRequired)
            {
                Invoke(new DataReceivedEventHandler(p_OutputDataReceived), new object[] { sender, e });
                return;
            }

            this.rtbExecStatus.Text += (e.Data ?? string.Empty) + Environment.NewLine;
            this.rtbExecStatus.SelectionStart = this.rtbExecStatus.Text.Length;
            this.rtbExecStatus.ScrollToCaret();
        }

        private void toolBtnOpen_Click(object sender, EventArgs e)
        {
            openFileDialog1.ShowDialog();

            if (openFileDialog1.FileName == "")
                return;

            curFileName = openFileDialog1.FileName;
            FormMain_Load(sender, e);
        }

        private void toolBtnSetup_Click(object sender, EventArgs e)
        {
            FormSQLDiagPath formSQLDiagPath = new FormSQLDiagPath();
            formSQLDiagPath.ShowDialog();
            sqlDiagPath = formSQLDiagPath.SQLDiagPath;
            MessageBox.Show(sqlDiagPath, "SQLDiag Path", MessageBoxButtons.OK);
        }

        private void toolBtnStop_Click(object sender, EventArgs e)
        {
            IntPtr windowHandle = IntPtr.Zero;
            Process[] processes = Process.GetProcessesByName("SQLdiag");
            if (processes.Length > 0)
            {
                windowHandle = processes[0].MainWindowHandle;
            }

            SetForegroundWindow(windowHandle);


            SendKeys.SendWait("^C^C");

            this.toolBtnStop.Enabled = false;
            this.toolButtonStart.Enabled = true;

        }

        private void KillProcess()
        {
            try
            {
                if ((null != p) && !p.HasExited)
                {
                    p.Kill();
                }
            }
            catch (Exception ex)
            {
                this.rtbExecStatus.Text = ex.Message;
            }
        }
        //private void FormMain_KeyPress(object sender, KeyPressEventArgs e)
        //{
        //    if ((ModifierKeys & Keys.Control) == Keys.Control)
        //    {
        //        if ((e.KeyChar=='c') || (e.KeyChar=='C'))
        //        {
        //            this.KillProcess();
        //        }
        //    }
        //}
    }//class
}//namespace
